feat(miner-hands): compose an immutable per-attempt acceptance-criteria document - #4449
Conversation
…ia document Before a coding-agent driver starts editing, the miner needs to pin down — immutably, so the agent cannot quietly redefine its own success bar mid-attempt — what "done" means for this attempt. This adds the pure composition step that folds the two already-shipped Phase 2 primitives into one document: - the sanitized PromptPacket (prompt-packet.ts, the analyze→coding-agent boundary membrane), and - the FeasibilityGateResult go/raise/avoid verdict (feasibility.ts). Producing the document is this module's job; writing it into the attempt worktree (JSONbored#4269) and handing it to the driver interface (JSONbored#4262) are separate concerns, so this stays pure and side-effect-free like the rest of the engine. Decisions made per the issue's open questions: - Serialization is deterministic, canonically-ordered JSON (not markdown), so a checksum recorded alongside stays byte-stable — the success bar is a machine-verifiable target a self-review step must prove did not move. - One fixed filename, ACCEPTANCE_CRITERIA_FILENAME. - The built document is deep-frozen so it cannot be mutated in-memory for the lifetime of the attempt. - Only a `go` verdict authorizes the attempt to start, so only `go` gets a file written (shouldWriteAcceptanceCriteria); a raise/avoid document is still returned with writable=false so a caller can log why it was skipped. Redaction is delegated to sanitizePromptPacketField rather than re-implemented, since this document is as exposed to a prompt-injectable session as the packet. - packages/gittensory-engine/src/miner/acceptance-criteria.ts: the composer. - packages/gittensory-engine/src/index.ts: barrel re-export of the API. - test/unit/miner-acceptance-criteria.test.ts: full coverage. Closes JSONbored#4271
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #4449 +/- ##
=======================================
Coverage 93.97% 93.97%
=======================================
Files 415 416 +1
Lines 37335 37342 +7
Branches 13647 13647
=======================================
+ Hits 35086 35093 +7
Misses 1594 1594
Partials 655 655
🚀 New features to boost your workflow:
|
|
Tip 🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩 ✅ Gittensory review result - approve/merge recommendedReview updated: 2026-07-09 19:22:06 UTC
✅ Suggested Action - Approve/Merge
Review summary Nits — 5 non-blocking
Linked issue satisfactionAddressed Review context
Contributor next steps
Signal definitions
🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by Gittensory, a quiet PR intelligence layer for OSS maintainers.
|
Closes #4271
What
Before a coding-agent driver (#4262's interface; #4266/#4267's implementations) starts editing, the miner needs to pin down — immutably, so the agent can't quietly redefine its own success bar mid-attempt — what "done" means for this attempt. This adds the pure composition step that folds the two already-shipped Phase 2 primitives into a single acceptance-criteria document:
PromptPacket(prompt-packet.ts:26, the analyze→coding-agent "boundary membrane"), andFeasibilityGateResultgo/raise/avoid verdict (feasibility.ts:21-26).This is the direct input to
preparePlanTemplate'scoding-agentstep (plan-templates.ts:84, which runs afterbranch-createand beforelocal-test). Producing the document is this module's job; writing it into the attempt worktree (#4269) and handing it to the driver interface (#4262) are separate concerns, so this stays pure and side-effect-free like the rest ofgittensory-engine.Decisions (the issue's open questions, resolved here)
serializeAcceptanceCriteriaemits stable-key-order JSON with a trailing newline so a recorded checksum stays byte-stable across processes.ACCEPTANCE_CRITERIA_FILENAME(acceptance-criteria.json).Object.freeze, arrays copied+frozen) so it can't be mutated in-memory for the lifetime of the attempt; the byte-stable serialization is what a caller checksums on disk.go: araise/avoidverdict means the attempt should not start, so no criteria file is written.buildAcceptanceCriteriastill returns a document withwritable: falseso a caller can log why it was skipped;shouldWriteAcceptanceCriteriais the single gate for the write itself.Redaction is delegated to
sanitizePromptPacketFieldrather than re-implemented, since this document is exactly as exposed to a prompt-injectable coding-agent session as the prompt packet already is (idempotent on already-sanitized text).API
buildAcceptanceCriteria({ promptPacket, feasibility }) => AcceptanceCriteria— pure, deep-frozen.serializeAcceptanceCriteria(doc) => string— deterministic canonical JSON + trailing newline.shouldWriteAcceptanceCriteria(verdict) => boolean— thego-only write gate.ACCEPTANCE_CRITERIA_FILENAME,ACCEPTANCE_CRITERIA_VERSION.Files
packages/gittensory-engine/src/miner/acceptance-criteria.ts— the composer.packages/gittensory-engine/src/index.ts— barrel re-export.test/unit/miner-acceptance-criteria.test.ts— tests (composition from both inputs, redaction pass-through, raise/avoid non-writable cases, deep-freeze/immutability, deterministic serialization).Testing
8/8 tests pass; typecheck clean. New engine file is fully covered (7/7 lines, 3/3 functions, no uncovered branches).